/* ** por compatibilidad se omiten tildes **
================================================================================
 TRABAJO PRACTICO 3 - System Programming - ORGANIZACION DE COMPUTADOR II - FCEN
================================================================================
  definicion de funciones del manejador de memoria
*/

#include "mmu.h"

void* punt;

 void* dame_pagina_libre(){
 	int i; 
 	void* aux = punt;
 	//dejo inicializada en 0x0 la pagina que pido.
 	char* res = (char*) aux;
 	for (i = 0; i < 0x1000; i++){
 		res[i]=0x00;
 	}
 	punt+=0x1000;
 	return aux;
}

void* ubicacion(unsigned int i, unsigned int j){
	void* res = (void*) (0x400000 + i*0x1000*78 + j*0x1000);
	return res;
}

void mmu_mapear_cuadrado(unsigned int i, unsigned int j, unsigned int pd, char jugador){
	unsigned int i_ant = i-1;
	unsigned int i_sig = i+1;
	void* ubic;
	if (i == 0){
		i_ant = 44;
	}
	if(i == 44){
		i_sig = 0;
	}
	if (jugador == 'A'){
		ubic = ubicacion(i,j);
		mmu_mapear_pagina(0x8000000, pd,(unsigned int)ubic,3);	
		ubic = ubicacion(i,j+1);
		mmu_mapear_pagina(0x8000000+0x1000,pd, (unsigned int)ubic,3);
		ubic = ubicacion(i_sig,j+1);
		mmu_mapear_pagina(0x8000000+0x2000,pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_ant,j+1);
		mmu_mapear_pagina(0x8000000+0x3000,pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_sig,j);
		mmu_mapear_pagina(0x8000000+0x4000,pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_ant,j);
		mmu_mapear_pagina(0x8000000+0x5000,pd,(unsigned int)ubic,3);
		ubic = ubicacion(i,j-1);
		mmu_mapear_pagina(0x8000000+0x6000,pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_ant,j-1);
		mmu_mapear_pagina(0x8000000+0x7000,pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_sig,j-1);
		mmu_mapear_pagina(0x8000000+0x8000,pd,(unsigned int)ubic,3);
	}
	else{
		ubic = ubicacion(i,j);
		mmu_mapear_pagina(0x8000000,(unsigned int) pd,(unsigned int)ubic,3);
		ubic = ubicacion(i,j-1);
		mmu_mapear_pagina(0x8000000+0x1000,(unsigned int)pd, (unsigned int)ubic,3);
		ubic = ubicacion(i_ant,j-1);
		mmu_mapear_pagina(0x8000000+0x2000,(unsigned int)pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_sig,j-1);
		mmu_mapear_pagina(0x8000000+0x3000,(unsigned int)pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_ant,j);
		mmu_mapear_pagina(0x8000000+0x4000,(unsigned int)pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_sig,j);
		mmu_mapear_pagina(0x8000000+0x5000,(unsigned int)pd,(unsigned int)ubic,3);
		ubic = ubicacion(i,j+1);
		mmu_mapear_pagina(0x8000000+0x6000,(unsigned int)pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_sig,j+1);
		mmu_mapear_pagina(0x8000000+0x7000,(unsigned int)pd,(unsigned int)ubic,3);
		ubic = ubicacion(i_ant,j+1);
		mmu_mapear_pagina(0x8000000+0x8000,(unsigned int)pd,(unsigned int)ubic,3);
	}
}

void cpy_code(char* dst, char* src, unsigned int tam){
	int i;
	for (i=0; i<tam; ++i){
		dst[i]=src[i];
	}
}

/*
	Borra el mapeo creado en la dirección virtual virtual utilizando cr3.
*/
void mmu_unmapear_pagina(unsigned int virtual, unsigned int cr3){
	unsigned int pd_offset=virtual>>22; //el offset en la page directory son los primeros 10 bits de la direccion virtual
	unsigned int pt_offset=virtual<<10;   //el offset en la page table son los 10 bits "del medio" de la direccion virtual
	pt_offset=pt_offset>>22;				  
	page_dir_entry* pd_entry=(page_dir_entry*)((cr3 & 0xFFFFF000)+4*pd_offset);
	page_table_entry* pt_entry=(page_table_entry*)(((pd_entry->dir_base)<<12)+4*pt_offset);
	pt_entry->p=0x0;
	tlbflush(); //limpio el cache, dado que actualice las estructuras de paginacion
}


/*
	Permite mapear la pagina fisica correspondiente a fisica en la direccion 
	virtual virtual utilizando cr3.
*/
void mmu_mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica, unsigned char priv_level){
	unsigned int offset=(virtual>>22);
	page_dir_entry* pd_entry=(page_dir_entry*)((cr3 & 0xFFFFF000)+4*offset); //los primeros 10 bits de la direccion virtual indican la entrada en la page_directory
	if(!(pd_entry->p)){
		pd_entry->p=0x1;			//pongo en 1 el bit de presente
		pd_entry->rw=0x1;			//pongo en 1 el bit de lectura/escritura
		pd_entry->us=priv_level;	//seteo el nivel de privilegio con el que puede ser accedida la pagina		
		pd_entry->dir_base = (unsigned int) dame_pagina_libre()>>12;	//como no estaba presente pido una nueva pagina
	}
	/*los diez bits "del medio" de la direccion logica indican el numero de entrada en la page table*/
	unsigned int n_entry=virtual<<10;
	n_entry=n_entry>>22;
	page_table_entry* pt_entry=(page_table_entry*)(((pd_entry->dir_base)<<12)+4*n_entry);
	pt_entry->p=0x1;
	pt_entry->rw=0x1;
	pt_entry->us=priv_level;
	pt_entry->dir_base_p=fisica>>12;
	tlbflush(); //limpio el cache, dado que actualice las estructuras de paginacion
}

/*
	La idea es hacer identity mapping de las direcciones 0x00000000 a 0x003FFFFF,
	que las cubro con 1024 paginas de 4K (0x400*0x1000=0x400000). Por lo tanto, 
	tengo que reservar memoria para un directorio de paginas y una tabla de 
	paginas ambas con 1024 entradas de 4 bytes.
*/

page_dir_entry* mmu_inicializar_dir_kernel(void){
	page_dir_entry* pd=(page_dir_entry*)0x27000;
	page_table_entry* pt=(page_table_entry*)0x28000;
	unsigned int dir=0x0;
	unsigned int i;
	pd[0]=(page_dir_entry)
	{
			(unsigned char)0x1,	
			(unsigned char)0x1,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned int)pt>>12,
	};

	for (i = 0; i < MAX_ENTRY; ++i){
		pt[i]=(page_table_entry)
		{
			(unsigned char)0x1,
			(unsigned char)0x1,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned char)0x0,
			(unsigned int)(dir>>12),
		};
		dir+=0x1000;
	}
	return pd;
}

page_dir_entry* mmu_inicializar_dir_zombi(char jugador, unsigned int pos_i, char z_type){
	page_dir_entry* pd = (page_dir_entry*) dame_pagina_libre();
	int i;
	for (i = 0; i < 0x003FFFFF; i+=0x1000){
		mmu_mapear_pagina(i,(unsigned int)pd,i,0);
	}
	char* dir_code;
	if(jugador == 'A'){
		mmu_mapear_cuadrado(pos_i,2,(unsigned int)pd,'A');
		switch(z_type){
			case 'G':
				dir_code = (char*) 0x10000;
				break;
			case 'M':
				dir_code = (char*) 0x11000;
				break;
			default:
				dir_code = (char*) 0x12000;
				break;
		}
	}
	else{
		mmu_mapear_cuadrado(pos_i,77,(unsigned int)pd,'B');
		switch(z_type){
			case 'G':
				dir_code = (char*) 0x13000;
				break;
			case 'M':
				dir_code = (char*) 0x14000;
				break;
			default:
				dir_code = (char*) 0x15000;
				break;
		}
	}
	void* ubic;
	if (jugador == 'A')	{
		ubic = ubicacion(pos_i,2);
	}
	else{
		ubic = ubicacion(pos_i,77);
	}
	mmu_mapear_pagina((unsigned int)ubic,rcr3(),(unsigned int)ubic,0);
	cpy_code((char*)ubic,dir_code,0x1000);
	mmu_unmapear_pagina((unsigned int)ubic,rcr3());
	return pd;
}

void mmu_inicializar(){
	punt = (void*) 0x100000;
}




